﻿jQuery.fn.matrix = function (options) {

    var settings = jQuery.extend({
        background: "none",
        grainSize: 10,
        colors: "all",
        animated: false,
        chars: ['.', '¸', '¹', '`', '*', '_', '°', 'ª', '^', '+', '±', '¢', '®', '"', 'υ',
                 '»', '½', '¾', 'h', 'e', '8', 's', 'p', '=', '/', '$', '§', 'ξ', 'u', '6', '9',
                 '5', 'y', 'j', 'd', 'q', 'H', 'ç', 'B', 'V', '8', 'Z', 'W', 'S',
                 '%', 'e', 'n', 'm', '&', 'à', 'ω', 'Ψ', 'o', '#', 'k', '●', '♦', '♥']
    }, options);

    function check_tag(node) {
        return node.nodeName.toLowerCase() == "img";
    };

    function getRandomChar() {
        return settings.chars[Math.floor(Math.random() * settings.chars.length)];
    }

    function getChar(r, g, b) {
        var gray = (r + g + b) / 3;
        var avail = settings.chars.length;

        var idx = Math.max(0, Math.round((avail * gray) / 255) - 1);

        return settings.chars[idx];
    }

    var style_added = false;
    

    function supports_canvas() {
        return !!document.createElement('canvas').getContext;
    }

    return this.each(function () {
        if (!supports_canvas()) {
            return false;

        }
        if (!check_tag(this)) {
            return;
        }

        var image_colors = [],
        matrix_cont,
        w_blocks,
        h_blocks,
        ctx,
        blocks_nodes;

        function doMatrix() {
            for (var j = h_blocks - 1; j >= 0; j--) {
                for (var i = 0; i < w_blocks; i++) {
                    var c = "";
                    if (j == 0) {
                        c = getRandomChar();
                    } else {
                        c = blocks_nodes[(j - 1) * w_blocks + i].innerHTML;
                    }

                    blocks_nodes[j * w_blocks + i].innerHTML = c;
                }
            }

            setTimeout(doMatrix, 100);
        }

        function fillColors() {
            var data;
            for (var _y = 0; _y < h_blocks; _y++) {
                for (var _x = 0; _x < w_blocks; _x++) {
                    var x = _x * settings.grainSize;
                    var y = _y * settings.grainSize;

                    data = ctx.getImageData(x, y, settings.grainSize, settings.grainSize).data;

                    var r_avg = 0,
                    g_avg = 0,
                    b_avg = 0;

                    for (var i = 0; i < data.length; i += 4) {
                        r_avg += data[i];
                        g_avg += data[i + 1];
                        b_avg += data[i + 2];
                    }
                    r_avg = Math.round(r_avg / (data.length / 4));
                    g_avg = Math.round(g_avg / (data.length / 4));
                    b_avg = Math.round(b_avg / (data.length / 4));

                    image_colors.push({ r: r_avg, g: g_avg, b: b_avg });
                }
            }
        }

        function createBlocks() {
            var fontSize = settings.fontSize == undefined ? settings.grainSize : settings.fontSize;
            for (var i = 0; i < image_colors.length; i++) {

                var block = document.createElement("b");
                jQuery(matrix_cont).append(block);

                var r = image_colors[i].r;
                var g = image_colors[i].g;
                var b = image_colors[i].b;

                block.innerHTML = getChar(r, g, b);

                switch (settings.colors) {
                    case "bn":
                        var gray = Math.round((r + g + b) / 3);
                        jQuery(block).css("color", "rgb(" + gray + "," + gray + "," + gray + ")");
                        break;
                    case "green":
                        jQuery(block).css("color", "rgb(0," + g + ",0)");
                        break;
                    case "all":
                        jQuery(block).css("color", "rgb(" + r + "," + g + "," + b + ")");
                        break;
                }
            }
            blocks_nodes = $.makeArray(jQuery(matrix_cont).children());
        }

        var canvas = document.createElement("canvas");
        matrix_cont = document.createElement("div");
        matrix_cont.className = this.className + " jquery-matrix-container";
        matrix_cont.style.backgroundColor = settings.background;
        ctx = canvas.getContext('2d');

        var w = this.width;
        var h = this.height;
        w_blocks = Math.floor(w / settings.grainSize);
        h_blocks = Math.floor(h / settings.grainSize);

        canvas.width = w;
        canvas.height = h;
        jQuery(matrix_cont).width(w);
        jQuery(matrix_cont).height(h);
        ctx.drawImage(this, 0, 0, w, h);

        fillColors(ctx);
        createBlocks();

        if (!style_added) {
            var style = document.createElement("style");
            var style_attr = ".jquery-matrix-container b {float:left;display:block;padding:0;margin:0;font-weight:normal;width:§px;height:§px;font-size:§px;line-height:§px}";
            style_attr = style_attr.replace(/§/g, settings.grainSize + "");
            jQuery(style).html(style_attr);
            jQuery("body").prepend("<style>" + style_attr + "</style>");
            style_added = true;
        }
        jQuery(this).replaceWith(matrix_cont);

        if (settings.animated)
            doMatrix();
    });
};